﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Map_Creator.RedoUndo.Actions;
using Map_Creator.XNA;
using Microsoft.Xna.Framework;

namespace Map_Creator.Painters
{
    public class TilePainter : IPainter
    {
        private Point previousPosition;
        private MapInfo.Map associatedMap;
        private Point hoveredTile = new Point(-1, -1);

        private TilePaintAction currentAction;

        public TilePainter(MapInfo.Map associatedMap)
        {
            this.associatedMap = associatedMap;
        }

        public Rectangle OnMouseDown(Point position, MouseButtons buttons)
        {
            if (buttons != MouseButtons.Left) return Rectangle.Empty;
            if (!MapInfo.Map.SelectedMapFullyInitialized()) return Rectangle.Empty;

            var tileSet = associatedMap.CurrentTileSet;
            var layer = associatedMap.CurrentLayer;
            currentAction = new TilePaintAction(tileSet.SelectionSource, layer,tileSet.Selection.Rectangle, new List<Rectangle>());

            previousPosition = position;
            previousPosition.X += associatedMap.CurrentTileSet.Selection.Rectangle.Width;
            return OnMouseMove(position, buttons);
        }

        public Rectangle OnMouseMove(Point position, MouseButtons buttons)
        {
            if (!MapInfo.Map.SelectedMapFullyInitialized()) return Rectangle.Empty;

            var prevHoveredTile = hoveredTile;
            hoveredTile = position;

            var invalidationRectangle = Rectangle.Empty;

            if (prevHoveredTile != hoveredTile)
            {
                //invalidate previous shadow rect
                invalidationRectangle = new Rectangle(prevHoveredTile.X, prevHoveredTile.Y,
                                                      MapInfo.Map.SelectedMap.CurrentTileSet.Selection.Rectangle.Width,
                                                      MapInfo.Map.SelectedMap.CurrentTileSet.Selection.Rectangle.Height);

                //invalidate shadow rect
                invalidationRectangle = MathEx.JoinRectangles(invalidationRectangle,
                                                              new Rectangle(prevHoveredTile.X, prevHoveredTile.Y,
                                                                            MapInfo.Map.SelectedMap.CurrentTileSet.
                                                                                Selection.Rectangle.Width,
                                                                            MapInfo.Map.SelectedMap.CurrentTileSet.
                                                                                Selection.Rectangle.Height));
            }

            if (buttons != MouseButtons.Left) return invalidationRectangle;

            var newPosition = true;
            while (newPosition)
            {
                newPosition = false;
                var source = associatedMap.CurrentTileSet.Selection.Rectangle;
                var drawPosition = new Point();

                if (Math.Abs(position.X - previousPosition.X) >= source.Width)
                {
                    newPosition = true;
                    drawPosition.Y = previousPosition.Y;
                    if (position.X > previousPosition.X)
                        drawPosition.X = previousPosition.X + source.Width;
                    else
                        drawPosition.X = previousPosition.X - source.Width;
                }
                else if (Math.Abs(position.Y - previousPosition.Y) >= source.Height)
                {
                    newPosition = true;
                    drawPosition.X = previousPosition.X;
                    if (position.Y > previousPosition.Y)
                        drawPosition.Y = previousPosition.Y + source.Height;
                    else
                        drawPosition.Y = previousPosition.Y - source.Height;
                }

                if (newPosition)
                {
                    previousPosition = drawPosition;
                    var layer = associatedMap.CurrentLayer;
                    // Paint
                    var destRect = new Rectangle(drawPosition.X, drawPosition.Y,
                                                 MathEx.Clamp(drawPosition.X + source.Width, 0, layer.data.Size.X) -
                                                 drawPosition.X,
                                                 MathEx.Clamp(drawPosition.Y + source.Height, 0, layer.data.Size.Y) -
                                                 drawPosition.Y);

                    //ActionHandler.MainActionHandler.AddActionAndExecute(new TilePaintAction(tileSet.SelectionSource,
                    //                                                                       layer, source, destRect));

                    currentAction.AddSingleDestAndExecute(destRect);

                    /*
                    for (int i = 0; i < source.Width && position.X + i < associatedMap.GridSize.X; i++)
                    {
                        for (int j = 0; j < source.Height && position.Y + j < associatedMap.GridSize.Y; j++)
                        {
                            layer[position.X + i, position.Y + j] = tileSet.SelectionSource[source.Location.X + i, source.Location.Y + j];
                            var asAuto = layer[position.X + i, position.Y + j] as UpdateableTile;
                            if (asAuto != null)
                            {
                                asAuto.UpdateValue(layer.data, new Point(position.X + i, position.Y + j));
                            }
                        }
                    }
                    */
                    invalidationRectangle = MathEx.JoinRectangles(invalidationRectangle,
                                                 new Rectangle(
                                                     MathEx.Clamp(drawPosition.X - 1, 0, associatedMap.GridSize.X),
                                                     MathEx.Clamp(drawPosition.Y - 1, 0, associatedMap.GridSize.Y),
                                                     MathEx.Clamp(source.Width + 1, 0,
                                                                  associatedMap.GridSize.X - source.Width - 1),
                                                     MathEx.Clamp(source.Height + 1, 0,
                                                                  associatedMap.GridSize.Y - source.Height - 1)));
                }
            }
            return invalidationRectangle;
        }

        public Rectangle OnMouseLeave()
        {
            var prevTile = hoveredTile;
            if (prevTile != new Point(-1, -1))
            {
                hoveredTile = new Point(-1, -1);
                return new Rectangle(prevTile.X, prevTile.Y, associatedMap.CurrentTileSet.Selection.Rectangle.Width,
                    associatedMap.CurrentTileSet.Selection.Rectangle.Height);
            }
            return Rectangle.Empty;
        }

        public Rectangle OnMouseUp()
        {
            if (currentAction != null)
            {
                ActionHandler.MainActionHandler.AddAction(currentAction);
            }
            return Rectangle.Empty;
        }

        public void OnDraw(XnaGraphics xnaGraphics, Rectangle destination)
        {
            if (!MapInfo.Map.SelectedMapFullyInitialized()) return;

            if (hoveredTile != new Point(-1, -1))
            {
                var tileSetSelection = associatedMap.CurrentTileSet.Selection.Rectangle;

                var tileSet = associatedMap.CurrentTileSet;
                var tileSize = MapInfo.Map.SelectedMap.TileSize;
                var color = Color.Multiply(Color.White, .5f);
                for (int i = hoveredTile.X;
                     i < hoveredTile.X + tileSetSelection.Width &&
                     i < associatedMap.GridSize.X;
                     i++)
                {
                    for (int j = hoveredTile.Y;
                         j < hoveredTile.Y + tileSetSelection.Height && 
                         j < associatedMap.GridSize.Y;
                         j++)
                    {
                        
                        var position = new Point(tileSetSelection.Location.X + (i - hoveredTile.X),
                                                 tileSetSelection.Location.Y + (j - hoveredTile.Y));

                        var tile = tileSet.SelectionSource[position.X, position.Y];
                        if (tile == null) continue;
                        tile.DrawTile(xnaGraphics, MapInfo.Map.SelectedMap,
                                      new Rectangle(i * tileSize, j * tileSize, tileSize, tileSize),
                                      color);
                    }
                }
            }
        }
    }
}